package com.zyj.java1;

/**
 * @program: JVMDemo
 * @ClassName StringTest5
 * @author: YaJun
 * @Date: 2021 - 12 - 28 - 10:58
 * @Package: com.zyj.java1
 * @Description: 字符串的拼接
 */
public class StringTest5 {

    public static void main(String[] args) {
        String s1 = "a" + "b" + "c";    // 编译期优化为 abc，然后放入常量池
        String s2 = "abc";  // 肯定是放入常量池

//        System.out.println(s1 == s2); // true
//        System.out.println(s1.equals(s2));  // true

//        test2();
//        test5();
        test6();
    }

    public static void test2() {
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";
        // 如果拼接符号的前后出现了变量，则相当于在堆空间当中new String()，具体的内容为拼接的结果：javaEEhadoop
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);   // true;
        System.out.println(s3 == s5);   // false
        System.out.println(s3 == s6);   // false
        System.out.println(s3 == s7);   // false
        System.out.println(s5 == s6);   // false;
        System.out.println(s5 == s7);   // false
        System.out.println(s6 == s7);   // false

        // intern(): 判断字符串常量池中是否存在javaEEhadoop值，如果存在，则返回常量池中的javaEEhadoop的地址。
        // 如果字符串常量池中不存在javaEEhadoop，则在常量池中创建一个javaEEhadoop，并返回此对象的地址。
        String s8 = s6.intern();
        System.out.println(s3 == s8);   // true;

    }


    public static void test3() {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        /**
         * 如下的s1 + s2的执行细节
         * ① StringBuilder sb = new StringBuilder();
         * ② sb.append("a");
         * ③ sb.append("b");
         * ④ sb.toString()   --> 约等于 new String("ab");
         *
         * 补充：在jdk5.0之后使用的是StringBuilder，在jdk5.0之前使用的是StringBuffer
         */
        String s4 = s1 + s2;
        System.out.println(s3 == s4);   // false
    }

    /**
     * 1. 字符串拼接操作不一定使用的是StringBuilder！
     *  如果拼接符号两端都是字符串常量或常量引用，则仍然使用编译期优化。即非StringBuilder的方式
     * 2. 针对于final修饰类、方法、基本数据类型、引用数据类型变量的量的结构时，能使用上final就时候建议使用上。
     *  可以在类加载时就可以将其初始化
     */
    public static void test4() {
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;
        System.out.println(s3 == s4);   // true
    }

    // 练习
    public static void test5() {
        String s1 = "javaEEhadoop";
        String s2 = "hadoop";

        String s3 = s1 + "hadoop";
        System.out.println(s1 == s3);   // false

        final String s4 = "javaEE"; // s4：常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5);   // true;

    }

    /**
     * 体会执行效率：通过StringBuilder的append方式添加字符串的效率要远高于使用String的字符串拼接方式！
     * 详情：① StringBuilder的append()方式，自始至终只创建过一个StringBuilder对象
     *      使用String的字符串拼接方式：创建了多个StringBuilder和String对象
     *      ② 使用String的字符串拼接方式：内存中由于创建了较多的StringBuilder和String对象，内存占用更大；
     *      如果进行GC，需要花费额外的时间
     *
     * 改进的空间：在实际开发中，如果基本确定要前前后后添加的字符串长度不高于某个限定值highLevel的情况下，建议使用构造器实例化：
     *      StringBuilder sb = new StringBuilder(highLevel); // new char[highLevel]
     **/
    public static void test6() {
        long start = System.currentTimeMillis();

//        method1(100000);    // 5220
        method2(100000);    // 0

        long end = System.currentTimeMillis();
        System.out.println("花费的时间为：" + (end - start));
    }

    private static void method2(int highLevel) {
        // 只需要创建一个StringBuilder
        StringBuilder src = new StringBuilder();
        for (int i = 0; i < highLevel; i++) {
            src.append("a");
        }
    }

    public static void method1(int highLevel) {
        String src = "";
        for (int i = 0; i < highLevel; i++) {
            src = src + "a";    // 每次循环都会创建一个StringBuilder、String
        }
    }




}
